{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 184 Kind介绍及WSL安"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[kind](https://kind.sigs.k8s.io/)\n",
    "\n",
    "- 工具定位: kind是一个使用Docker容器\"节点\"运行本地Kubernetes集群的工具，主要用于测试Kubernetes本身，也可用于本地开发或CI\n",
    "- 安装要求: 需要go 1.16+和docker/podman/nerdctl环境\n",
    "- 使用场景: 适合单机模拟学习k8s，但不建议用于生产环境部署\n",
    "- 工作原理: 通过Docker容器模拟多台物理机，构建分布式集群环境"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! kind create cluster"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 185 安装Docker"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 186 安装Kind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go install sigs.k8s.io/kind@latest"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://docs.docker.com/go/wsl2/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 187 K8S有啥用，它和Docker的关系是什么"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 层级关系：\n",
    "  - Docker：提供容器化基础（镜像打包与运行）\n",
    "  - K8S：构建在容器之上的集群管理系统\n",
    "- 镜像兼容性：两者使用相同镜像格式规范，Docker镜像可直接用于K8S\n",
    "- 工具生态：除Docker工具外，其他符合OCI标准的工具也可生成兼容镜像"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 188 制作镜像，Docker的使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 189 用Kind创建K8S集群"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 集群概念"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 集群组成: 由多个服务器(物理/虚拟)组成，用于部署多个微服务。例如：5台服务器部署3个微服务，其中1个需要部署在所有5台，另外2个各需部署在3台。\n",
    "- 管理机制: K8S负责确保微服务正常运行，当某台服务器故障时，自动将服务调度到其他服务器重新启动。\n",
    "- 核心组件:\n",
    "  - Control Plane: 主节点(master)，维护集群信息并接收操作指令\n",
    "  - Compute Machines: 工作节点(worker)，执行实际计算任务\n",
    "- 层级关系:\n",
    "  - 集群包含1个control plane和多个worker节点\n",
    "  - 每个worker运行多个Pod\n",
    "  - 每个Pod包含多个容器\n",
    "  - 容器内运行应用程序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 用Kind模拟集群"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 工具选择: 使用Kind+Docker组合模拟集群，通过Docker容器模拟物理节点\n",
    "- 环境准备:\n",
    "  - 启动WSL(Linux子系统)\n",
    "  - 以管理员身份运行Docker Desktop\n",
    "  - 打开VS Code用于编辑配置文件\n",
    "- 资源管理:\n",
    "  - 所有K8S对象都称为Resource\n",
    "  - 每个Resource有唯一name标识\n",
    "  - 通过namespace实现资源隔离，默认namespace为\"default\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建集群"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```yaml\n",
    "apiVersion: kind.x-k8s.io/v1alpha4\n",
    "kind: Cluster\n",
    "name: dqq\n",
    "nodes:\n",
    "  - role: control-plane\n",
    "  - role: worker\n",
    "  - role: worker\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 配置文件规范:\n",
    "  - 格式: YAML/JSON(推荐YAML)\n",
    "  - 必须字段:\n",
    "    - apiVersion: API分组及版本(如kind.x-k8s.io/v1alpha4)\n",
    "    - kind: 资源类型(Cluster)\n",
    "    - name: 集群名称(如dqq)\n",
    "    - nodes: 节点配置列表\n",
    "- 节点配置:\n",
    "  - role: control-plane: 控制平面节点\n",
    "  - role: worker: 工作节点\n",
    "  - 示例配置: 1个control-plane + 2个worker"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建K8S集群, [cluster.yaml](./190/deploy/cluster.yaml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```yaml\n",
    "apiVersion: kind.x-k8s.io/v1alpha4\n",
    "kind: Cluster\n",
    "name: demokind\n",
    "nodes:\n",
    "  - role: control-plane\n",
    "  - role: worker\n",
    "  - role: worker\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 创建命令: `kind create cluster --config <yaml文件路径>`\n",
    "- 创建过程:\n",
    "  - 准备节点镜像\n",
    "  - 写入配置\n",
    "  - 启动control-plane\n",
    "  - 安装CNI网络插件\n",
    "  - 安装StorageClass\n",
    "  - 加入worker节点\n",
    "- 资源考虑:\n",
    "  - 节点越多消耗资源越大\n",
    "  - 每个节点都会存储镜像副本，增加磁盘占用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 先下载镜像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! docker pull kindest/node:v1.34.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Creating cluster \"demokind\" ...\n",
      " • Ensuring node image (kindest/node:v1.34.0) 🖼  ...\n",
      " ✓ Ensuring node image (kindest/node:v1.34.0) 🖼\n",
      " • Preparing nodes 📦 📦 📦   ...\n",
      " ✓ Preparing nodes 📦 📦 📦 \n",
      " • Writing configuration 📜  ...\n",
      " ✓ Writing configuration 📜\n",
      " • Starting control-plane 🕹️  ...\n",
      " ✓ Starting control-plane 🕹️\n",
      " • Installing CNI 🔌  ...\n",
      " ✓ Installing CNI 🔌\n",
      " • Installing StorageClass 💾  ...\n",
      " ✓ Installing StorageClass 💾\n",
      " • Joining worker nodes 🚜  ...\n",
      " ✓ Joining worker nodes 🚜\n",
      "Set kubectl context to \"kind-demokind\"\n",
      "You can now use your cluster with:\n",
      "\n",
      "kubectl cluster-info --context kind-demokind\n",
      "\n",
      "Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/\n"
     ]
    }
   ],
   "source": [
    "! kind create cluster --config ./190/deploy/cluster.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 显示更多日志信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! kind create cluster --config ./190/deploy/cluster.yaml --retain --verbosity 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind\n"
     ]
    }
   ],
   "source": [
    "! kind get clusters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 删除集群"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Deleting cluster \"demokind\" ...\n",
      "Deleted nodes: [\"demokind-worker\" \"demokind-worker2\" \"demokind-control-plane\"]\n"
     ]
    }
   ],
   "source": [
    "! kind delete cluster --name demokind"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 清理 kubeconfig（可选但推荐）\n",
    "\n",
    "```shell\n",
    "kubectl config delete-context kind-<集群名称>\n",
    "kubectl config delete-cluster kind-<集群名称>\n",
    "kubectl config unset users.kind-<集群名称>\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES\n"
     ]
    }
   ],
   "source": [
    "! docker ps -a --filter \"name=kind-\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 验证清理结果\n",
    "\n",
    "```shell\n",
    "kind get clusters       # 应为空\n",
    "kubectl config get-contexts   # 不再有 kind-xxx 项\n",
    "docker ps -a | grep kind-     # 无残留节点容器\n",
    "docker network ls | grep kind # 仅当仍有输出时，手动 docker network rm kind\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 查看K8S控制平面"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 查看命令: `kubectl cluster-info --context <集群名称>`\n",
    "- 关键信息:\n",
    "  - 控制平面运行地址(如https://127.0.0.1:60559)\n",
    "  - CoreDNS服务地址\n",
    "- Docker验证:\n",
    "  - 在Docker Desktop中可看到对应容器\n",
    "  - 容器命名规则: <集群名称>-<节点类型>[序号]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CURRENT   NAME            CLUSTER         AUTHINFO        NAMESPACE\n",
      "*         kind-demokind   kind-demokind   kind-demokind   \n"
     ]
    }
   ],
   "source": [
    "! kubectl config get-contexts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*         kind-demokind   kind-demokind   kind-demokind   \n"
     ]
    }
   ],
   "source": [
    "! kubectl config get-contexts | grep demokind"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://127.0.0.1:35219/\n",
    "\n",
    "```json\n",
    "{\n",
    "  \"kind\": \"Status\",\n",
    "  \"apiVersion\": \"v1\",\n",
    "  \"metadata\": {},\n",
    "  \"status\": \"Failure\",\n",
    "  \"message\": \"forbidden: User \\\"system:anonymous\\\" cannot get path \\\"/\\\"\",\n",
    "  \"reason\": \"Forbidden\",\n",
    "  \"details\": {},\n",
    "  \"code\": 403\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://127.0.0.1:35219/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy\n",
    "\n",
    "```json\n",
    "{\n",
    "  \"kind\": \"Status\",\n",
    "  \"apiVersion\": \"v1\",\n",
    "  \"metadata\": {},\n",
    "  \"status\": \"Failure\",\n",
    "  \"message\": \"services \\\"kube-dns:dns\\\" is forbidden: User \\\"system:anonymous\\\" cannot get resource \\\"services/proxy\\\" in API group \\\"\\\" in the namespace \\\"kube-system\\\"\",\n",
    "  \"reason\": \"Forbidden\",\n",
    "  \"details\": {\n",
    "    \"name\": \"kube-dns:dns\",\n",
    "    \"kind\": \"services\"\n",
    "  },\n",
    "  \"code\": 403\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 查看集群节点"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 查询命令: `kind get nodes --name <集群名称>`\n",
    "- 节点组成:\n",
    "  - 1个control-plane节点\n",
    "  - N个worker节点(按配置数量)\n",
    "- 底层实现:\n",
    "  - 每个K8S节点对应一个Docker容器\n",
    "  - 容器镜像使用kindest/node\n",
    "  - 通过容器模拟物理节点功能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind-worker2\n",
      "demokind-control-plane\n",
      "demokind-worker\n"
     ]
    }
   ],
   "source": [
    "! kind get nodes --name demokind"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 190 K8S部署微服务"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 制作镜像"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 制作命令：使用`docker build -t 镜像名称:tag Dockerfile目录`命令制作镜像，其中`-t`参数指定镜像名称和版本号\n",
    "- 验证方法：通过Docker Desktop可以查看已制作的镜像，如生成的blog镜像（1.18GB）和golang基础镜像（837.23MB）\n",
    "- 注意点：此时镜像仅在Docker环境中存在，尚未与k8s集群建立关联"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! cd 190/blog && go get github.com/gin-gonic/gin@latest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! cd 190/blog && go mod tidy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! docker build -t demokind:v1.0.0 190/blog"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 把镜像导入k8s集群"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 导入命令：使用`kind load docker-image 镜像名称:版本号 --name 集群名称`将镜像导入指定k8s集群\n",
    "- 工作流程：命令会将镜像拷贝到集群所有节点（如示例中的control-plane、worker2、worker三个节点）\n",
    "- 耗时原因：镜像文件较大（每个约1GB多），导入过程较慢\n",
    "- 验证方法：可通过`kubectl get nodes`查看集群节点状态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "REPOSITORY     TAG       IMAGE ID       CREATED          SIZE\n",
      "demokind       v1.0.0    83e9781dc497   53 minutes ago   1.85GB\n",
      "kindest/node   v1.34.0   7416a61b42b1   4 weeks ago      1.45GB\n"
     ]
    }
   ],
   "source": [
    "! docker image ls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-worker2\", loading...\n",
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-control-plane\", loading...\n",
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-worker\", loading...\n"
     ]
    }
   ],
   "source": [
    "! kind load docker-image demokind:v1.0.0 --name demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                     STATUS   ROLES           AGE     VERSION\n",
      "demokind-control-plane   Ready    control-plane   5m23s   v1.34.0\n",
      "demokind-worker          Ready    <none>          6m26s   v1.34.0\n",
      "demokind-worker2         Ready    <none>          6m26s   v1.34.0\n"
     ]
    }
   ],
   "source": [
    "! kubectl get nodes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 部署程序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 部署文件内容解释, [dep-blog.yaml](./190/deploy/dep-blog.yaml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```yaml\n",
    "apiVersion: apps/v1   \n",
    "kind: Deployment \n",
    "\n",
    "metadata:  \n",
    "  name: demokind  # deploy的名称\n",
    "  labels:     # deploy的标签\n",
    "    app: demokind \n",
    "\n",
    "spec:   \n",
    "  replicas: 2   # pod副本数目\n",
    "  selector:    # 匹配什么样的pod\n",
    "    matchLabels:\n",
    "      app: demokind\n",
    "\n",
    "  template:   # 设置pod信息\n",
    "    metadata:\n",
    "      labels:  # pod的标签\n",
    "        app: demokind \n",
    "    spec:   \n",
    "      containers:   # pod里包含哪些容器\n",
    "        - name: demokind   # 容器名称\n",
    "          image: demokind:v1.0.0  # 容器里运行哪个镜像\n",
    "          imagePullPolicy: IfNotPresent   # 镜像拉取策略\n",
    "          resources:\n",
    "            requests:    # 控制该pod对硬件资源的利用\n",
    "              cpu: 10m  # 1个CPU核是1000m，10m相当于0.01个CPU\n",
    "\n",
    "      # nodeSelector:\n",
    "      #   hp: \"true\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `deploy`名称和标签 \n",
    "  - 命名规则：同一命名空间下每个资源的`name`必须唯一\n",
    "  - 标签作用：通过`key-value`形式标记资源，示例中`app:blog`表示该`deployment`管理所有带此标签的`pod`\n",
    "  - 选择机制：不同资源可共用相同标签，实现批量选择\n",
    "- `deployment`配置项 \n",
    "  - 副本数量：`replicas:2`表示创建2个`pod`副本，分布在不同的服务器上实现负载均衡和容错\n",
    "  - 选择器：`selector.matchLabels`定义该`deployment`管理的`pod`标签条件\n",
    "  - 高可用：多副本设计既分担压力又提供故障转移能力\n",
    "- `template`配置项 \n",
    "  - `pod`的标签\n",
    "    - 标签对应：`pod`的`metadata.labels`必须与`deployment`的`selector.matchLabels`匹配\n",
    "    - 层级关系：`template`下的`metadata/spec`描述`pod`，区别于最外层的`deployment`描述\n",
    "  - `pod`的容器 \n",
    "    - 容器定义：使用数组形式定义，每个元素以`-`开头，可定义多个容器\n",
    "    - 镜像策略：`imagePullPolicy:IfNotPresent`表示本地存在镜像时不从远程拉取\n",
    "    - 关键配置：必须明确指定镜像名称和版本号（如blog:v1.0.0）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 部署文件应用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `kubectl`命令介绍 \n",
    "  - 基础命令：`kubectl apply -f 配置文件.yaml`将配置应用到集群\n",
    "  - 通信流程：命令先发送到控制平面，再由控制平面分发到各工作节点\n",
    "- 查看`pod`\n",
    "  - 查看命令：`kubectl get pod`查看pod基本信息，`-o wide`显示详细信息（包括IP和节点）\n",
    "  - 状态验证：READY列显示1/1表示容器已就绪，AGE显示运行时间\n",
    "- 访问Web服务 \n",
    "  - 内部访问：通过`pod IP`（如10.244.2.2）和端口（5678）在集群内部访问服务\n",
    "  - 外部访问：需要额外创建ingress资源才能从外部访问\n",
    "  - 服务验证：示例中访问返回HTML页面，包含动态参数（name）和服务器IP信息\n",
    "  - 多节点测试：可在不同worker节点上验证服务访问，确认负载均衡效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/demokind created\n"
     ]
    }
   ],
   "source": [
    "! cd 190/deploy && kubectl apply -f dep-blog.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE\n",
      "demokind-7cb76d9f85-c2k68   1/1     Running   0          4s\n",
      "demokind-7cb76d9f85-lzbft   1/1     Running   0          4s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![190.1](./images/190.1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE     IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-c2k68   1/1     Running   0          2m39s   10.244.1.2   demokind-worker    <none>           <none>\n",
      "demokind-7cb76d9f85-lzbft   1/1     Running   0          2m39s   10.244.2.2   demokind-worker2   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![190.2](./images/190.2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 191 service"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用K8S来部署线上微服务的好处"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 自动恢复机制：当Pod因程序问题或服务器宕机而终止时，K8S会自动在可用节点上重新启动Pod。例如演示中删除Pod后，系统立即创建了新Pod（原IP 10.244.2.2 → 新IP 10.244.2.3），整个过程耗时约7秒。\n",
    "- IP动态性注意：Pod重启后会分配新IP，因此直接通过IP访问Pod不可靠，需要通过Service提供固定访问入口。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-c2k68   1/1     Running   0          21m   10.244.1.2   demokind-worker    <none>           <none>\n",
      "demokind-7cb76d9f85-lzbft   1/1     Running   0          21m   10.244.2.2   demokind-worker2   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pod \"demokind-7cb76d9f85-c2k68\" deleted from default namespace\n"
     ]
    }
   ],
   "source": [
    "! kubectl delete pod demokind-7cb76d9f85-c2k68"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-2lh7w   1/1     Running   0          7s    10.244.1.3   demokind-worker    <none>           <none>\n",
      "demokind-7cb76d9f85-lzbft   1/1     Running   0          21m   10.244.2.2   demokind-worker2   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## service配置文件示例, [svc-blog.yaml](./191/deploy/svc-blog.yaml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Service基础配置"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```yaml\n",
    "apiVersion: v1\n",
    "kind: Service\n",
    "\n",
    "metadata:\n",
    "  name: svc-kinddemo    # service名称\n",
    "\n",
    "spec:\n",
    "  type: ClusterIP # service类型。ClusterIP即service提供一个k8s的虚拟IP，在k8s内部通过该虚拟IP来访问服务。不管pod所处的节点如何变化，该IP是固定的\n",
    "  ports:\n",
    "    - name: http\n",
    "      port: 5678    # 外界访问使用的端口\n",
    "      targetPort: 5678    # 代码里使用的端口\n",
    "      protocol: TCP\n",
    "  selector: # 该service匹配哪些pod\n",
    "    app: demokind # 指定pod标签\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "核心字段：\n",
    "- apiVersion: v1：标识K8S API版本\n",
    "- kind: Service：资源类型声明\n",
    "- metadata.name：自定义服务名称（如svc-blog）\n",
    "- spec.type: ClusterIP：创建集群内部虚拟IP\n",
    "- spec.ports：端口映射配置，包含：\n",
    "  - port: 5678：外部访问端口\n",
    "  - targetPort: 5678：Pod实际监听端口\n",
    "  - protocol: TCP：传输协议类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pod选择机制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "selector匹配规则：通过`spec.selector.app: demokind`选择具有`app=demokind`标签的Pod，与Deployment的标签选择器机制一致。演示中Pod因Deployment配置了对应标签而被正确关联。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 端口映射原理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 双端口配置：\n",
    "  - 外部端口（port）与容器端口（targetPort）可不同（如5678→8080）\n",
    "  - 演示保持相同端口号（5678→5678）实现直通访问\n",
    "- IP稳定性：Service提供的ClusterIP在生命周期内固定，解决了Pod IP动态变化的问题"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## service部署"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 部署方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 部署命令: 使用`kubectl apply -f`指定样本文件进行部署，与部署deployment方式相同\n",
    "- 查看命令: 通过`kubectl get service`查看已创建的service\n",
    "- 特性验证:\n",
    "  - 删除pod后，service的Cluster IP保持不变（示例中保持为10.96.139.199）\n",
    "  - 系统会自动创建新的pod替代被删除的pod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "service/svc-kinddemo created\n"
     ]
    }
   ],
   "source": [
    "! cd 191/deploy && kubectl apply -f svc-blog.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE\n",
      "kubernetes     ClusterIP   10.96.0.1      <none>        443/TCP    51m\n",
      "svc-kinddemo   ClusterIP   10.96.72.153   <none>        5678/TCP   19s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get service"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### service特性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- IP稳定性: service的Cluster IP固定不变（示例中为10.96.72.153）\n",
    "- 负载均衡: service会自动将请求分发到后端不同的pod（示例中pod IP为10.244.1.4和10.244.2.3）\n",
    "- 访问限制: Cluster IP只能在k8s集群内部访问"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-2lh7w   1/1     Running   0          22m   10.244.1.3   demokind-worker    <none>           <none>\n",
      "demokind-7cb76d9f85-lzbft   1/1     Running   0          43m   10.244.2.2   demokind-worker2   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 删除pod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pod \"demokind-7cb76d9f85-2lh7w\" deleted from default namespace\n"
     ]
    }
   ],
   "source": [
    "! kubectl delete pod demokind-7cb76d9f85-2lh7w "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pod \"demokind-7cb76d9f85-lzbft\" deleted from default namespace\n"
     ]
    }
   ],
   "source": [
    "! kubectl delete pod demokind-7cb76d9f85-lzbft"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-gxctt   1/1     Running   0          59s   10.244.1.4   demokind-worker    <none>           <none>\n",
      "demokind-7cb76d9f85-strr4   1/1     Running   0          3s    10.244.2.3   demokind-worker2   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE\n",
      "kubernetes     ClusterIP   10.96.0.1      <none>        443/TCP    52m\n",
      "svc-kinddemo   ClusterIP   10.96.72.153   <none>        5678/TCP   63s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get service"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 访问Service"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 访问方式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![191.1](./images/191.1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 访问命令: 使用curl命令访问service的Cluster IP和端口（示例为10.96.72.153:5678）\n",
    "- 响应特点: 请求会被随机分配到后端不同的pod，实现负载均衡\n",
    "  - 第一次访问由10.244.2.3响应\n",
    "  - 第二次访问由10.244.2.3响应\n",
    "  - 第二次访问由10.244.1.4响应\n",
    "  - 后续访问会继续在这两个pod间轮询"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### service优势"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 固定访问点: 提供不变的Cluster IP，屏蔽后端pod的变化\n",
    "- 自动负载均衡: 自动将请求分发到多个pod实例\n",
    "- 高可用性: 当pod被删除或故障时，service会自动将流量路由到健康的pod"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 192 node selector"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[svc-blog.yaml](./191/deploy/svc-blog.yaml)\n",
    "- 标签选择机制：之前学习过通过pod标签和selector机制来选择pod，现在扩展学习节点标签的使用方法\n",
    "- 应用场景：通过节点标签可以实现pod的定向调度，例如将资源密集型服务部署到高性能服务器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 给节点打标签"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 添加标签命令：使用`kubectl label nodes <node-name> <key>=<value>`格式\n",
    "  - 示例：`kubectl label nodes dqq-worker hp=true`\n",
    "  - 注意：标签值都是字符串类型，即使写`true`也需要加引号表示字符串\n",
    "- 批量添加：可以一次性添加多个标签，如`kubectl label nodes <node-name> l2=v2 l3=v3`\n",
    "- 查看标签：\n",
    "  - 查看单个节点：`kubectl label node <node-name> --list`\n",
    "  - 查看特定标签节点：`kubectl get node -l <key>=<value>`\n",
    "- 标签分类：\n",
    "  - 系统默认标签：如`kubernetes.io/os=linux`\n",
    "  - 自定义标签：如`hp=true`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                     STATUS     ROLES           AGE   VERSION\n",
      "demokind-control-plane   Ready      control-plane   70m   v1.34.0\n",
      "demokind-worker          NotReady   <none>          71m   v1.34.0\n",
      "demokind-worker2         NotReady   <none>          71m   v1.34.0\n"
     ]
    }
   ],
   "source": [
    "! kubectl get nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "node/demokind-worker labeled\n"
     ]
    }
   ],
   "source": [
    "! kubectl label nodes demokind-worker hp=true"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "node/demokind-worker labeled\n"
     ]
    }
   ],
   "source": [
    "! kubectl label nodes demokind-worker hp=true l2=v2 l3=v3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "kubernetes.io/arch=amd64\n",
      "kubernetes.io/hostname=demokind-worker\n",
      "kubernetes.io/os=linux\n",
      "l2=v2\n",
      "l3=v3\n",
      "beta.kubernetes.io/arch=amd64\n",
      "beta.kubernetes.io/os=linux\n",
      "hp=true\n"
     ]
    }
   ],
   "source": [
    "! kubectl label node demokind-worker --list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME              STATUS   ROLES    AGE   VERSION\n",
      "demokind-worker   Ready    <none>   75m   v1.34.0\n"
     ]
    }
   ],
   "source": [
    "! kubectl get node -l hp=true"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## pod的nodeSelector使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " [dep-blog.yaml](./192/deploy/dep-blog.yaml)\n",
    "\n",
    "- 配置方法：\n",
    "  - 在pod模板的`spec`部分添加`nodeSelector`字段\n",
    "  - 示例：`nodeSelector: {hp: \"true\"}`（注意字符串需要引号）\n",
    "- 注意事项：\n",
    "  - YAML解析会将`true`识别为布尔值，必须加引号强制转为字符串\n",
    "  - 需要调整`replicas`数量匹配可用节点数（示例中从2改为1）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 应用配置"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 应用变更：\n",
    "  - 使用`kubectl apply -f <file>`应用修改后的配置\n",
    "  - 系统会自动调整pod数量匹配`replicas`设置\n",
    "- 验证方法：\n",
    "  - `kubectl get pod -o wide`查看pod调度节点\n",
    "  - 删除pod后新创建的pod仍会调度到符合标签要求的节点\n",
    "- 调度特性：\n",
    "  - 严格匹配`nodeSelector`指定的标签\n",
    "  - 如果没有符合要求的节点，pod将处于Pending状态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/demokind configured\n"
     ]
    }
   ],
   "source": [
    "! cd 192/deploy && kubectl apply -f dep-blog.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE              NOMINATED NODE   READINESS GATES\n",
      "demokind-5994ff964b-9zwm2   1/1     Running   0          15s   10.244.1.5   demokind-worker   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pod \"demokind-5994ff964b-9zwm2\" deleted from default namespace\n"
     ]
    }
   ],
   "source": [
    "! kubectl delete pod demokind-5994ff964b-9zwm2 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE              NOMINATED NODE   READINESS GATES\n",
      "demokind-5994ff964b-7rx4j   1/1     Running   0          7s    10.244.1.6   demokind-worker   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 193 Ingress-Nginx"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从集群外部发起访问, 需要装`Ingress Controller`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 删除之前的集群"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind\n"
     ]
    }
   ],
   "source": [
    "! kind get clusters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Deleting cluster \"demokind\" ...\n",
      "Deleted nodes: [\"demokind-control-plane\" \"demokind-worker2\" \"demokind-worker3\" \"demokind-worker\"]\n"
     ]
    }
   ],
   "source": [
    "! kind delete cluster --name demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "error: cannot delete context kind-demokind, not in /home/ningliu/.kube/config\n",
      "exit status 1\n",
      "error: cannot delete cluster kind-demokind, not in /home/ningliu/.kube/config\n",
      "exit status 1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Property \"users.kind-demokind\" unset.\n"
     ]
    }
   ],
   "source": [
    "! kubectl config delete-context kind-demokind\n",
    "! kubectl config delete-cluster kind-demokind\n",
    "! kubectl config unset users.kind-demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "No kind clusters found.\n"
     ]
    }
   ],
   "source": [
    "! kind get clusters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建全新集群, [cluster.yaml](./193/deploy/cluster.yaml)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Creating cluster \"demokind\" ...\n",
      " • Ensuring node image (kindest/node:v1.34.0) 🖼  ...\n",
      " ✓ Ensuring node image (kindest/node:v1.34.0) 🖼\n",
      " • Preparing nodes 📦 📦 📦 📦   ...\n",
      " ✓ Preparing nodes 📦 📦 📦 📦 \n",
      " • Writing configuration 📜  ...\n",
      " ✓ Writing configuration 📜\n",
      " • Starting control-plane 🕹️  ...\n",
      " ✓ Starting control-plane 🕹️\n",
      " • Installing CNI 🔌  ...\n",
      " ✓ Installing CNI 🔌\n",
      " • Installing StorageClass 💾  ...\n",
      " ✓ Installing StorageClass 💾\n",
      " • Joining worker nodes 🚜  ...\n",
      " ✓ Joining worker nodes 🚜\n",
      "Set kubectl context to \"kind-demokind\"\n",
      "You can now use your cluster with:\n",
      "\n",
      "kubectl cluster-info --context kind-demokind\n",
      "\n",
      "Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂\n"
     ]
    }
   ],
   "source": [
    "! kind create cluster --config ./193/deploy/cluster.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 安装`Ingress Nginx Controller`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/\n",
    "- https://github.com/kubernetes/ingress-nginx/blob/main/deploy/static/provider/cloud/deploy.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 使用旧版1.11.2的`ingress-controller.yaml`, 新版1.13.3的费死劲也没跑出来结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [ingress-controller.yaml](./193/deploy/ingress-controller.yaml)文件中的 `nodeSelector` 配置分析\n",
    "\n",
    "在 `ingress-controller.yaml` 里，`nodeSelector` 一共出现了三处，分别作用于三个不同的工作负载（一个 `Deployment` 和两个 `Job`）：\n",
    "\n",
    "1. **`Deployment` → `ingress-nginx-controller`**\n",
    "\n",
    "   ```yaml\n",
    "   spec:\n",
    "     template:\n",
    "       spec:\n",
    "         nodeSelector:\n",
    "           kubernetes.io/os: linux\n",
    "   ```\n",
    "\n",
    "   - 该 `nodeSelector` 要求控制器 Pod 必须调度到带有标签 `kubernetes.io/os=linux` 的节点。\n",
    "   - 这意味着整个 Ingress Controller 仅能在 Linux 节点上运行，避免被调度到 Windows 节点或其它不兼容环境。\n",
    "   - 对于 kind 或大多数 Linux 集群来说，节点默认就带这个标签，因此通常不会阻碍调度。\n",
    "\n",
    "2. **`Job` → `ingress-nginx-admission-create`**\n",
    "\n",
    "   ```yaml\n",
    "   spec:\n",
    "     template:\n",
    "       spec:\n",
    "         nodeSelector:\n",
    "           kubernetes.io/os: linux\n",
    "   ```\n",
    "\n",
    "   - 这个 Job 负责创建 Admission Webhook 所需的证书 Secret。\n",
    "   - 同样限定 Pod 只能调度到 Linux 节点。\n",
    "\n",
    "3. **`Job` → `ingress-nginx-admission-patch`**\n",
    "\n",
    "   \n",
    "\n",
    "   ```yaml\n",
    "   spec:\n",
    "     template:\n",
    "       spec:\n",
    "         nodeSelector:\n",
    "           kubernetes.io/os: linux\n",
    "   ```\n",
    "\n",
    "   - 该 Job 用于给 `ValidatingWebhookConfiguration` 打补丁。\n",
    "   - 也被限制在 Linux 节点上执行。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "影响与注意事项\n",
    "\n",
    "- **核心目的**：Ingress-NGINX 官方镜像目前只支持 Linux 环境，设置 `nodeSelector` 能确保工作负载不会意外落到其它 OS（例如 Windows）上导致失败。\n",
    "- **默认标签**：Kubernetes 在所有节点上都会自动注入 `kubernetes.io/os` 标签，值为节点实际的操作系统类型（如 `linux`、`windows`）。\n",
    "- **与其它调度机制的关系**：`nodeSelector` 是最直接的调度约束。如果后续还需要更细粒度控制（如 Node 污点/容忍、`nodeAffinity`、`topologySpreadConstraints` 等），要确保不会与该 `nodeSelector` 冲突。\n",
    "- **修改建议**：除非你的集群存在非 Linux 的节点并希望在其上运行 Ingress（目前镜像不支持），否则无需移除或更改该配置。如果确实要允许或排除特定节点，可以改成更具体的标签组合。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 根据标签选择结点\n",
    "```yaml\n",
    "      nodeSelector:\n",
    "        kubernetes.io/os: linux\n",
    "```\n",
    "改为\n",
    "```yaml\n",
    "      nodeSelector:\n",
    "        ingress-ready: \"true\" # control-plane添加这个标签\n",
    "        kubernetes.io/os: linux\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## control-plane添加`ingress-ready: \"true\" `标签"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                     STATUS   ROLES           AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION                     CONTAINER-RUNTIME\n",
      "demokind-control-plane   Ready    control-plane   13m   v1.34.0   172.18.0.5    <none>        Debian GNU/Linux 12 (bookworm)   6.6.36.3-microsoft-standard-WSL2   containerd://2.1.3\n",
      "demokind-worker          Ready    <none>          12m   v1.34.0   172.18.0.2    <none>        Debian GNU/Linux 12 (bookworm)   6.6.36.3-microsoft-standard-WSL2   containerd://2.1.3\n",
      "demokind-worker2         Ready    <none>          12m   v1.34.0   172.18.0.3    <none>        Debian GNU/Linux 12 (bookworm)   6.6.36.3-microsoft-standard-WSL2   containerd://2.1.3\n",
      "demokind-worker3         Ready    <none>          12m   v1.34.0   172.18.0.4    <none>        Debian GNU/Linux 12 (bookworm)   6.6.36.3-microsoft-standard-WSL2   containerd://2.1.3\n"
     ]
    }
   ],
   "source": [
    "! kubectl get node -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "node/demokind-control-plane labeled\n"
     ]
    }
   ],
   "source": [
    "! kubectl label nodes demokind-control-plane ingress-ready=true"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "kubernetes.io/arch=amd64\n",
      "kubernetes.io/hostname=demokind-control-plane\n",
      "kubernetes.io/os=linux\n",
      "node-role.kubernetes.io/control-plane=\n",
      "node.kubernetes.io/exclude-from-external-load-balancers=\n",
      "beta.kubernetes.io/arch=amd64\n",
      "beta.kubernetes.io/os=linux\n",
      "ingress-ready=true\n"
     ]
    }
   ],
   "source": [
    "! kubectl label node demokind-control-plane --list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 应用`ingress-controller.yaml`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "namespace/ingress-nginx created\n",
      "serviceaccount/ingress-nginx created\n",
      "serviceaccount/ingress-nginx-admission created\n",
      "role.rbac.authorization.k8s.io/ingress-nginx created\n",
      "role.rbac.authorization.k8s.io/ingress-nginx-admission created\n",
      "clusterrole.rbac.authorization.k8s.io/ingress-nginx created\n",
      "clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created\n",
      "rolebinding.rbac.authorization.k8s.io/ingress-nginx created\n",
      "rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created\n",
      "clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created\n",
      "clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created\n",
      "configmap/ingress-nginx-controller created\n",
      "service/ingress-nginx-controller created\n",
      "service/ingress-nginx-controller-admission created\n",
      "deployment.apps/ingress-nginx-controller created\n",
      "job.batch/ingress-nginx-admission-create created\n",
      "job.batch/ingress-nginx-admission-patch created\n",
      "ingressclass.networking.k8s.io/nginx created\n",
      "validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created\n"
     ]
    }
   ],
   "source": [
    "! cd 193/deploy && kubectl apply -f ingress-controller.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! cd 193/deploy && kubectl delete -f ingress-controller.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                 STATUS   AGE\n",
      "default              Active   11m\n",
      "ingress-nginx        Active   2m5s\n",
      "kube-node-lease      Active   11m\n",
      "kube-public          Active   11m\n",
      "kube-system          Active   11m\n",
      "local-path-storage   Active   11m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get namespaces"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                                        READY   STATUS              RESTARTS   AGE   IP           NODE                     NOMINATED NODE   READINESS GATES\n",
      "ingress-nginx-admission-create-qx6sb        0/1     Completed           2          92s   10.244.1.3   demokind-worker3         <none>           <none>\n",
      "ingress-nginx-admission-patch-l645r         0/1     Completed           2          92s   10.244.2.3   demokind-worker          <none>           <none>\n",
      "ingress-nginx-controller-6b585f4cf4-qb7gf   0/1     ContainerCreating   0          92s   <none>       demokind-control-plane   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -n ingress-nginx -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ">  另外2个事Job，负责生成/更新 webhook 证书，完成后状态是 Completed，Pod 会结束并被回收，所以现在列表里只剩下控制器本身"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                                        READY   STATUS      RESTARTS   AGE     IP           NODE                     NOMINATED NODE   READINESS GATES\n",
      "ingress-nginx-admission-create-qx6sb        0/1     Completed   2          5m42s   10.244.1.3   demokind-worker3         <none>           <none>\n",
      "ingress-nginx-admission-patch-l645r         0/1     Completed   2          5m42s   10.244.2.3   demokind-worker          <none>           <none>\n",
      "ingress-nginx-controller-6b585f4cf4-qb7gf   1/1     Running     0          5m42s   10.244.0.5   demokind-control-plane   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -n ingress-nginx -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "pod的STATUS不是Running或者Completed; 可能是拉取镜像失败"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 获取镜像加速地址 , https://docker.aityp.com/, 替换`registry.k8s.io`\n",
    "- 挂代理手动拉取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "REPOSITORY     TAG       IMAGE ID       CREATED          SIZE\n",
      "lottery        v1.0.0    79480f560bc4   31 minutes ago   1.85GB\n",
      "demokind       v1.0.0    83e9781dc497   8 hours ago      1.85GB\n",
      "golang         latest    ab1f5c47de0f   4 weeks ago      1.25GB\n",
      "kindest/node   v1.34.0   7416a61b42b1   5 weeks ago      1.45GB\n"
     ]
    }
   ],
   "source": [
    "! docker image ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 部署博客的pod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind\n"
     ]
    }
   ],
   "source": [
    "! kind get clusters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-worker3\", loading...\n",
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-worker2\", loading...\n",
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-worker\", loading...\n",
      "Image: \"demokind:v1.0.0\" with ID \"sha256:83e9781dc4977d0752d5b2ac3057256b7032b6293a5935a073376a3a870f2e71\" not yet present on node \"demokind-control-plane\", loading...\n"
     ]
    }
   ],
   "source": [
    "! kind load docker-image demokind:v1.0.0 --name demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 131,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/demokind created\n"
     ]
    }
   ],
   "source": [
    "! cd 193/deploy && kubectl apply -f dep-blog.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 132,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          76s   10.244.1.4   demokind-worker3   <none>           <none>\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          76s   10.244.2.4   demokind-worker    <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 133,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "service/svc-kinddemo created\n"
     ]
    }
   ],
   "source": [
    "! cd 193/deploy && kubectl apply -f svc-blog.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 134,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE\n",
      "kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP    22m\n",
      "svc-kinddemo   ClusterIP   10.96.168.200   <none>        5678/TCP   76s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get svc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 配置[`ingress-nginx.yaml`](./193/deploy/ingress-nginx.yaml)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ingress.networking.k8s.io/example-ingress created\n"
     ]
    }
   ],
   "source": [
    "! cd 193/deploy && kubectl apply -f ingress-nginx.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 集群外部访问项目"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 136,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          17s   10.244.1.4   demokind-worker3   <none>           <none>\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          17s   10.244.2.4   demokind-worker    <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 303,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "100    89  100    89    0     0  11180      0 --:--:-- --:--:-- --:--:--     0:--:-- 12714\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<h2>\n",
      "    你好\n",
      "    <br>\n",
      "    这里是blog系统\n",
      "    <br>\n",
      "    Server IP: 10.244.3.4\n",
      "</h2>"
     ]
    }
   ],
   "source": [
    "! curl localhost:80/blog"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 307,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "100    94  100    94    0     0  14735      0 --:--:-- --:--:-- --:--:--     0 --:--:-- --:--:-- 15666\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<h2>\n",
      "    你好shale\n",
      "    <br>\n",
      "    这里是blog系统\n",
      "    <br>\n",
      "    Server IP: 10.244.2.6\n",
      "</h2>"
     ]
    }
   ],
   "source": [
    "! curl localhost:80/blog?name=shale"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 309,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "100    94  100    94    0     0  21090      0 --:--:-- --:--:-- --:--:--     0- --:--:-- --:--:-- 23500\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<h2>\n",
      "    你好shale\n",
      "    <br>\n",
      "    这里是blog系统\n",
      "    <br>\n",
      "    Server IP: 10.244.3.4\n",
      "</h2>"
     ]
    }
   ],
   "source": [
    "! curl localhost/blog?name=shale"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![193.1](./images/193.1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 194 多服务混合部署"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 之前blog的部署"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 入口配置：Ingress可以配置多个微服务路径，通过路径前缀进行区分。示例中`/blog`路径对应`svc-kinddemo`服务，端口号为5678。\n",
    "- 部署流程：需要先编写代码，然后通过Docker工具制作镜像，最后导入k8s集群。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过地址路径区别服务, 再配置一个服务"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 部署lottery "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 写好相关代码\n",
    "- 制作镜像(注意修改端口号)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! docker build -t lottery:v1.0.0 193/lottery"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 导入集群中"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 139,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Image: \"lottery:v1.0.0\" with ID \"sha256:5a0e1416a30fba3b67c0dedec8a485ccd4bfe5571bcb4238f82e6b76416d2e73\" not yet present on node \"demokind-worker3\", loading...\n",
      "Image: \"lottery:v1.0.0\" with ID \"sha256:5a0e1416a30fba3b67c0dedec8a485ccd4bfe5571bcb4238f82e6b76416d2e73\" not yet present on node \"demokind-worker2\", loading...\n",
      "Image: \"lottery:v1.0.0\" with ID \"sha256:5a0e1416a30fba3b67c0dedec8a485ccd4bfe5571bcb4238f82e6b76416d2e73\" not yet present on node \"demokind-worker\", loading...\n",
      "Image: \"lottery:v1.0.0\" with ID \"sha256:5a0e1416a30fba3b67c0dedec8a485ccd4bfe5571bcb4238f82e6b76416d2e73\" not yet present on node \"demokind-control-plane\", loading...\n"
     ]
    }
   ],
   "source": [
    "! kind load docker-image lottery:v1.0.0 --name demokind"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 部署文件[`dep-lottery.yaml`](./193/deploy/dep-lottery.yaml)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "service/svc-lottery created\n"
     ]
    }
   ],
   "source": [
    "! cd 194/deploy && kubectl apply -f svc-lottery.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [`svc-lottery.yaml`](./193/deploy/svc-lottery.yaml)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/lottery created\n"
     ]
    }
   ],
   "source": [
    "! cd 194/deploy && kubectl apply -f dep-lottery.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                        READY   STATUS    RESTARTS   AGE     IP           NODE               NOMINATED NODE   READINESS GATES\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          3m11s   10.244.1.4   demokind-worker3   <none>           <none>\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          3m11s   10.244.2.4   demokind-worker    <none>           <none>\n",
      "lottery-6b98f4c89-cmxtk     1/1     Running   0          75s     10.244.1.5   demokind-worker3   <none>           <none>\n",
      "lottery-6b98f4c89-qkmnn     1/1     Running   0          75s     10.244.3.4   demokind-worker2   <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE    SELECTOR\n",
      "kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP    23m    <none>\n",
      "svc-kinddemo   ClusterIP   10.96.168.200   <none>        5678/TCP   116s   app=demokind\n",
      "svc-lottery    ClusterIP   10.96.97.71     <none>        5679/TCP   8s     app=lottery\n"
     ]
    }
   ],
   "source": [
    "! kubectl get svc -o wide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [`ingress-nginx.yaml`](./193/deploy/ingress-nginx.yaml)增加lottery的配置, 重新应用`ingress-nginx.yaml`即可"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ingress.networking.k8s.io/example-ingress unchanged\n"
     ]
    }
   ],
   "source": [
    "! cd 194/deploy && kubectl apply -f ingress-nginx.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 145,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "100    92  100    92    0     0  30893      0 --:--:-- --:--:-- --:--:-- 46000\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<h2>\n",
      "    你好\n",
      "    <br>\n",
      "    这里是lottery系统\n",
      "    <br>\n",
      "    Server IP: 10.244.3.4\n",
      "</h2>"
     ]
    }
   ],
   "source": [
    "! curl localhost/lottery"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 147,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "100    98  100    98    0     0  24987      0 --:--:-- --:--:-- --:--:--     0-:--:-- 32666\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<h2>\n",
      "    你好累了\n",
      "    <br>\n",
      "    这里是lottery系统\n",
      "    <br>\n",
      "    Server IP: 10.244.1.5\n",
      "</h2>"
     ]
    }
   ],
   "source": [
    "! curl localhost/lottery?name=累了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "100    89  100    89    0     0  35585      0 --:--:-- --:--:-- --:--:-- 44  0500\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<h2>\n",
      "    你好\n",
      "    <br>\n",
      "    这里是blog系统\n",
      "    <br>\n",
      "    Server IP: 10.244.1.4\n",
      "</h2>"
     ]
    }
   ],
   "source": [
    "! curl localhost/blog"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 195 Pod自动扩容"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自动扩缩容"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 修改配置文件, [dep-blog.yaml](./195/deploy/dep-blog.yaml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```yaml\n",
    "    spec:   \n",
    "      containers:   # pod里包含哪些容器\n",
    "        - name: demokind   # 容器名称\n",
    "          image: demokind:v1.0.0  # 容器里运行哪个镜像\n",
    "          imagePullPolicy: IfNotPresent   # 镜像拉取策略\n",
    "          resources:\n",
    "            requests:    # 控制该pod对硬件资源的利用\n",
    "              cpu: 10m  # 1个CPU核是1000m，10m相当于0.01个CPU\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "自动扩缩容原理：\n",
    "- 当服务器请求量高导致CPU/内存负载过高时，需要增加Pod副本来分担压力\n",
    "- 扩容触发条件基于单个容器配置的CPU限制值，而非宿主机整体CPU利用率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/demokind unchanged\n"
     ]
    }
   ],
   "source": [
    "! kubectl apply -f 195/deploy/dep-blog.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 水平Pod(HorizontalPodAutoscaler)自动扩缩容, [hpa-blog.yaml](./195/deploy/hpa-blog.yaml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- HPA配置参数：\n",
    "  - `maxReplicas:5`：最大副本数限制\n",
    "  - `minReplicas:2`：最小副本数保证\n",
    "  - `scaleTargetRef`：通过`apiVersion/kind/name`三元组指定监控目标\n",
    "  - `targetCPUUtilizationPercentage:50`：CPU利用率阈值（基于单个容器配置的限制值）\n",
    "- 扩容逻辑：\n",
    "  - 当两个副本的CPU利用率都超过5m（10m的50%）时，会创建第三个副本\n",
    "  - 以此类推，直到达到最大副本数限制\n",
    "  - 请求量下降后，副本数会逐步缩减至最小值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 177,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "horizontalpodautoscaler.autoscaling/hpa-blog unchanged\n"
     ]
    }
   ],
   "source": [
    "! kubectl apply -f 195/deploy/hpa-blog.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 安装监控工具"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! wget -O 195/deploy/metrics-server.yaml https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[metrics-server.yaml](./195/deploy/metrics-server.yaml)\n",
    "\n",
    "```yaml\n",
    "    spec:\n",
    "      containers:\n",
    "      - args:\n",
    "        - --cert-dir=/tmp\n",
    "        - --secure-port=10250\n",
    "        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\n",
    "        - --kubelet-use-node-status-port\n",
    "        - --metric-resolution=15s\n",
    "        - --kubelet-insecure-tls # 加一行\n",
    "        image: registry.k8s.io/metrics-server/metrics-server:v0.8.0\n",
    "        imagePullPolicy: IfNotPresent\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 184,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "serviceaccount/metrics-server unchanged\n",
      "clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader unchanged\n",
      "clusterrole.rbac.authorization.k8s.io/system:metrics-server unchanged\n",
      "rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader unchanged\n",
      "clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator unchanged\n",
      "clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server unchanged\n",
      "service/metrics-server unchanged\n",
      "deployment.apps/metrics-server configured\n",
      "apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io unchanged\n"
     ]
    }
   ],
   "source": [
    "! cd 195/deploy && kubectl apply -f metrics-server.yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 191,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "metrics-server-576c8c997c-5g4pz                  0/1     ContainerCreating   0          53s   <none>       demokind-worker3         <none>           <none>\n",
      "metrics-server-75b5d87c9d-r9h5p                  0/1     Running             0          10m   10.244.3.5   demokind-worker2         <none>           <none>\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod -o wide -n kube-system | grep metrics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 194,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME       REFERENCE             TARGETS       MINPODS   MAXPODS   REPLICAS   AGE\n",
      "hpa-blog   Deployment/demokind   cpu: 0%/50%   2         5         2          9m37s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get hpa"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 启动后等一会, 刚开始会显示`unknown`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 压测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 193,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind-7cb76d9f85-rq296   1/1     Running   0          32m\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          32m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod | grep demokind"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [压测脚本](./195/deploy/stress_test.sh)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME       REFERENCE             TARGETS        MINPODS   MAXPODS   REPLICAS   AGE\n",
      "hpa-blog   Deployment/demokind   cpu: 10%/50%   2         5         2          10m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get hpa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 199,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME       REFERENCE             TARGETS         MINPODS   MAXPODS   REPLICAS   AGE\n",
      "hpa-blog   Deployment/demokind   cpu: 305%/50%   2         5         2          10m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get hpa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 198,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind-7cb76d9f85-qbcct   1/1     Running   0          6s\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          33m\n",
      "demokind-7cb76d9f85-stwg5   1/1     Running   0          6s\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          33m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod | grep demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 200,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind-7cb76d9f85-qbcct   1/1     Running   0          30s\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          34m\n",
      "demokind-7cb76d9f85-stwg5   1/1     Running   0          30s\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          34m\n",
      "demokind-7cb76d9f85-whltg   1/1     Running   0          14s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod | grep demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 241,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME       REFERENCE             TARGETS         MINPODS   MAXPODS   REPLICAS   AGE\n",
      "hpa-blog   Deployment/demokind   cpu: 152%/50%   2         5         5          13m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get hpa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 239,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind-7cb76d9f85-qbcct   1/1     Running   0          2m47s\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          36m\n",
      "demokind-7cb76d9f85-stwg5   1/1     Running   0          2m47s\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          36m\n",
      "demokind-7cb76d9f85-whltg   1/1     Running   0          2m31s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod | grep demokind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 233,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name:                                                  hpa-blog\n",
      "Namespace:                                             default\n",
      "Labels:                                                <none>\n",
      "Annotations:                                           <none>\n",
      "CreationTimestamp:                                     Thu, 02 Oct 2025 06:33:24 +0800\n",
      "Reference:                                             Deployment/demokind\n",
      "Metrics:                                               ( current / target )\n",
      "  resource cpu on pods  (as a percentage of request):  188% (18m) / 50%\n",
      "Min replicas:                                          2\n",
      "Max replicas:                                          5\n",
      "Deployment pods:                                       5 current / 5 desired\n",
      "Conditions:\n",
      "  Type            Status  Reason            Message\n",
      "  ----            ------  ------            -------\n",
      "  AbleToScale     True    ReadyForNewScale  recommended size matches current size\n",
      "  ScalingActive   True    ValidMetricFound  the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request)\n",
      "  ScalingLimited  True    TooManyReplicas   the desired replica count is more than the maximum replica count\n",
      "Events:\n",
      "  Type     Reason                        Age                   From                       Message\n",
      "  ----     ------                        ----                  ----                       -------\n",
      "  Warning  FailedGetScale                8m20s (x19 over 12m)  horizontal-pod-autoscaler  deployments.apps \"dep-blog\" not found\n",
      "  Warning  FailedComputeMetricsReplicas  7m35s (x3 over 8m5s)  horizontal-pod-autoscaler  invalid metrics (1 invalid out of 1), first error is: failed to get cpu resource metric value: failed to get cpu utilization: unable to get metrics for resource cpu: unable to fetch metrics from resource metrics API: the server is currently unable to handle the request (get pods.metrics.k8s.io)\n",
      "  Warning  FailedGetResourceMetric       7m20s (x4 over 8m5s)  horizontal-pod-autoscaler  failed to get cpu utilization: unable to get metrics for resource cpu: unable to fetch metrics from resource metrics API: the server is currently unable to handle the request (get pods.metrics.k8s.io)\n",
      "  Normal   SuccessfulRescale             2m20s                 horizontal-pod-autoscaler  New size: 4; reason: cpu resource utilization (percentage of request) above target\n",
      "  Normal   SuccessfulRescale             2m4s                  horizontal-pod-autoscaler  New size: 5; reason: cpu resource utilization (percentage of request) above target\n"
     ]
    }
   ],
   "source": [
    "! kubectl describe hpa hpa-blog"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 247,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME       REFERENCE             TARGETS         MINPODS   MAXPODS   REPLICAS   AGE\n",
      "hpa-blog   Deployment/demokind   cpu: 124%/50%   2         5         5          14m\n"
     ]
    }
   ],
   "source": [
    "! kubectl get hpa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 249,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "demokind-7cb76d9f85-qbcct   1/1     Running   0          3m23s\n",
      "demokind-7cb76d9f85-rq296   1/1     Running   0          37m\n",
      "demokind-7cb76d9f85-stwg5   1/1     Running   0          3m23s\n",
      "demokind-7cb76d9f85-tznl7   1/1     Running   0          37m\n",
      "demokind-7cb76d9f85-whltg   1/1     Running   0          3m7s\n"
     ]
    }
   ],
   "source": [
    "! kubectl get pod | grep demokind"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Go (gonb)",
   "language": "go",
   "name": "gonb"
  },
  "language_info": {
   "codemirror_mode": "",
   "file_extension": ".go",
   "mimetype": "text/x-go",
   "name": "go",
   "nbconvert_exporter": "",
   "pygments_lexer": "",
   "version": "go1.24.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
